#pragma once
#include <string>
#include <cstring>
#include <sys/socket.h> // 核心Socket API（socket(), bind(), connect()等）
#include <netinet/in.h> // IPv4/IPv6 地址结构（struct sockaddr_in）
#include <arpa/inet.h>  // IP地址转换函数（inet_pton(), inet_ntop()）
#include <unistd.h>     // 文件描述符操作（close(), read(), write()）
#include <sys/wait.h>   //多进程需要

#include "log.hpp"

using namespace LogModule;

static std::string defaultip = "127.0.0.1"; // 默认的IP1与端口
static const uint16_t defaultport = 8080;

#define Die(code)   \
    do              \
    {               \
        exit(code); \
    } while (0)

#define CONV(v) (struct sockaddr *)(v)
class TcpClient
{
public:
    TcpClient(uint16_t port = defaultport)
        : _port(port),
          is_running(false)
    {
    }
    ~TcpClient()
    {
    }
    void InitClient()
    {
        _sockfd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "_socket create error";
            Die(1);
        }
        LOG(LogLevel::INFO) << "_socket create success, sockfd if :" << _sockfd;

        // 创建一个struct sockaddr_in结构体并给他们赋予初始值
        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(defaultport);                  // 进行本地到网络的转化
        local.sin_addr.s_addr = inet_addr(defaultip.c_str()); // 把点分十进制的IP地址进行转化

        // 与UDP一样，客户端不需要进行手动绑定

        // TCP是一种面向连接的协议，所以我们需要建立连接
        // 客户端进行连接操作
        int n = ::connect(_sockfd, CONV(&local), sizeof(local));
        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "connect error";
            Die(4);
        }
        LOG(LogLevel::INFO) << "connect success, sockfd is : " << _sockfd;
    }

    void Start()
    {
        is_running = true;
        std::string message;
        while (is_running)
        {
            // 设置一个缓冲区方便我们写入，读取数据
            char buffer[1024];
            std::cout << "input message: ";
            std::getline(std::cin, message);

            // 之后，我们就可以进行写入数据给对方了
            // int n=::write(_sockfd,message.c_str(),message.size());
            int n = ::send(_sockfd, message.c_str(), message.size(), 0);
            if (n > 0)
            {
                // 写入成功就要开始读了
                int m = ::read(_sockfd, buffer, sizeof(buffer));
                // 如果读取返回的消息也成功了
                if (m > 0)
                {
                    buffer[m] = 0;
                    std::cout << buffer << std::endl;
                }
                else
                {
                    break;
                }
            }
            else
            {
                break;
            }
        }
    }

private:
    int _sockfd; // 根据之前所学的UDP，我们可以知道，创建套接字其实就相当于打开了一个文件描述符。
    // 这个文件描述符时全双工的特性，也就是说，既可以读也可以写，所以我们这里就事先弄一个成员变量用来管理这个文件描述符
    bool is_running; // 服务端的运行状态
    uint16_t _port;
};